/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 combat_actor_model_cache.cpp

	$Header: /heroes4/combat_actor_model_cache.cpp $

	$NoKeywords: $

 ************************************************************************/

// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "combat_actor_model_cache.h"

#include <cassert>
#include <map>

#include "cache_base.h"
#include "combat_actor_model.h"
#include "combat_actor_model_definition.h"
#include "creature_traits.h"
#include "creature_type.h"
#include "enum_operations.h"
#include "hero_class_properties.h"
#include "missile_type.h"
#include "owned_ptr.h"
#include "ptr_cache.h"
#include "town_type.h"

// ------------------------------------------------------------------------------
// Unnamed namespace
// ------------------------------------------------------------------------------

namespace
{

	// ------------------------------------------------------------------------------
	// t_combat_actor_model_cache_data class
	// ------------------------------------------------------------------------------

	class t_combat_actor_model_cache_data
		:	public t_abstract_cache_data< t_combat_actor_model >, public t_cache_base
	{
	public:
		// Constructor
		t_combat_actor_model_cache_data( std::string const & name, double scale_ratio );

		virtual int get_size();
	private:
		// Types
		typedef t_pointer_cache< t_combat_actor_model_definition > t_definition_cache;

		// Data members
		double								m_scale_ratio;
		t_definition_cache					m_definition_cache;
		t_owned_ptr< t_combat_actor_model >	m_model_ptr;

		// Member functions
		virtual void					add_reference();
		virtual t_combat_actor_model *	do_get( t_progress_handler * handler_ptr );
		virtual void					remove_reference();
		virtual void                    release_memory();
	};

	// ------------------------------------------------------------------------------
	// array of actor model caches for creatures
	// ------------------------------------------------------------------------------
	class t_creature_caches
	{
	public:
		t_creature_caches( double scale );

		t_combat_actor_model_ptr get( t_creature_type creature ) const;
	protected:
		t_combat_actor_model_cache m_caches[k_creature_type_count];
	};

	inline t_combat_actor_model_ptr t_creature_caches::get( t_creature_type creature ) const
	{
		return m_caches[creature].get();
	}

	typedef std::map< double, t_creature_caches > t_creature_map;

	// ------------------------------------------------------------------------------
	// array of hero model caches
	// ------------------------------------------------------------------------------

	class t_hero_caches
	{
	public:
		t_hero_caches( double scale );

		t_combat_actor_model_ptr get( t_town_type alignment, bool sex, 
			                          t_hero_combat_model_type type ) const;
	protected:
		t_combat_actor_model_cache m_caches[k_town_type_count][2][k_hero_combat_model_count];
	};

	inline t_combat_actor_model_ptr t_hero_caches::get( t_town_type alignment, bool sex, 
			                                            t_hero_combat_model_type type ) const
	{
		return m_caches[alignment][sex][type].get();
	}

	typedef std::map<double, t_hero_caches> t_hero_model_map;
} // Unnamed namespace

// ------------------------------------------------------------------------------
// t_combat_actor_model_cache members
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_combat_actor_model_cache::t_combat_actor_model_cache() 
	: t_abstract_cache< t_combat_actor_model >(0)
{
}

t_combat_actor_model_cache::t_combat_actor_model_cache( std::string const & name, double scale_ratio )
	:	t_abstract_cache< t_combat_actor_model >( new t_combat_actor_model_cache_data( name, scale_ratio ) )
{
}

// ------------------------------------------------------------------------------
// t_combat_actor_model_cache_data members
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_combat_actor_model_cache_data::t_combat_actor_model_cache_data( std::string const & name, double scale_ratio )
	:	m_scale_ratio( scale_ratio ),
		m_definition_cache( name )
{
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_combat_actor_model_cache_data::add_reference()
{
	t_cache_base::add_reference();
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_combat_actor_model * t_combat_actor_model_cache_data::do_get( t_progress_handler * handler_ptr )
{
	if ( m_model_ptr.get() == 0 )
	{
		t_cached_ptr< t_combat_actor_model_definition > definition_ptr = m_definition_cache.get();
		if ( definition_ptr.get() == 0 )
			return 0;

		m_model_ptr.reset( new t_combat_actor_model( definition_ptr, m_scale_ratio ) );
	}

	return m_model_ptr.get();
}

int t_combat_actor_model_cache_data::get_size()
{
	return sizeof( t_combat_actor_model );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_combat_actor_model_cache_data::remove_reference()
{
	t_cache_base::remove_reference();
}

void t_combat_actor_model_cache_data::release_memory()
{
	m_model_ptr.reset();
}

// ------------------------------------------------------------------------------
// construct array of creature model caches.
// ------------------------------------------------------------------------------
t_creature_caches::t_creature_caches( double scale )
{
	t_creature_type creature;

	for (creature = t_creature_type(0); creature != k_creature_type_count; enum_incr(creature))
		m_caches[creature] = t_combat_actor_model_cache( get_keyword( creature ), scale );
}

// ------------------------------------------------------------------------------
// get actor model for a given creature
// ------------------------------------------------------------------------------
t_combat_actor_model_ptr get_combat_actor_model( t_creature_type creature, 
												 double			 scale_ratio )
{
	static t_creature_map    map;
	t_creature_map::iterator result;

	result = map.find( scale_ratio );
	if (result == map.end())
		result = map.insert( t_creature_map::value_type( scale_ratio, 
		                     t_creature_caches( scale_ratio ))).first;

	t_combat_actor_model_ptr ptr;

	ptr = result->second.get( creature );
	if (ptr.get() == 0 && creature != k_mage)
		ptr = get_combat_actor_model( k_mage, scale_ratio );
	return ptr;
}

// ------------------------------------------------------------------------------
// construct array of hero caches
// ------------------------------------------------------------------------------
t_hero_caches::t_hero_caches( double scale )
{
	static char const* type_keywords[] = { "fighter", "archer", "mage" };
	static char const* sex_keywords[] = { "female", "male" };
	t_town_type       town;
	int               sex;
	t_hero_combat_model_type type;
	std::string       name;

	for (town = t_town_type(0); town < k_town_type_count; enum_incr(town))
	{
		for (sex = 0; sex < 2; sex++)
		{
			for (type = t_hero_combat_model_type(0); type < k_hero_combat_model_count; 
			     enum_incr(type))
			{
				name = get_combat_type_keyword( town, type, sex == 1 );
				m_caches[town][sex][type] = t_combat_actor_model_cache( name, scale );
			}
		}
	}
}

// ------------------------------------------------------------------------------
// get actor model for a given hero
// ------------------------------------------------------------------------------
t_combat_actor_model_ptr get_combat_actor_model( t_town_type alignment,
												 bool		 is_male,
												 bool		 has_archery,
												 bool		 is_spellcaster,
												 double		 scale_ratio )
{
	static t_hero_model_map    map;
	t_hero_combat_model_type          type;
	t_hero_model_map::iterator result;

	if (is_spellcaster && alignment != k_town_might)
		type = k_model_spellcaster;
	else if (has_archery)
		type = k_model_archer;
	else
		type = k_model_fighter;

	result = map.find( scale_ratio );
	if (result == map.end())
		result = map.insert( t_hero_model_map::value_type(scale_ratio, 
		                     t_hero_caches( scale_ratio ))).first;

	t_combat_actor_model_ptr ptr;

	ptr = result->second.get( alignment, is_male, type );
	if (ptr.get() == 0)
		ptr = get_combat_actor_model( k_mage, scale_ratio );
	return ptr;
}

// ------------------------------------------------------------------------------
// get appropriate missile model
// ------------------------------------------------------------------------------
namespace
{
	class t_missile_caches
	{
	public:
		t_missile_caches( double scale_ratio );
		
		t_combat_actor_model_ptr get( t_missile_type type ) const;
		double                   get_scale() const;
	protected:
		t_combat_actor_model_cache m_caches[k_missile_type_count];
		double                     m_scale;
	};

	inline double t_missile_caches::get_scale() const
	{
		return m_scale;
	}

	inline t_combat_actor_model_ptr t_missile_caches::get( t_missile_type type ) const
	{
		return  m_caches[type].get();
	}
};

// ------------------------------------------------------------------------------
// get appropriate missile model
// ------------------------------------------------------------------------------
t_missile_caches::t_missile_caches( double scale_ratio )
{
	t_missile_type missile;
	std::string    name;

	m_scale = scale_ratio;
	for (missile = t_missile_type(0); missile < k_missile_type_count; enum_incr(missile))
	{
		name = "missile.";
		name += get_keyword( missile );
		m_caches[missile] = t_combat_actor_model_cache( name, scale_ratio );
	}
}

// ------------------------------------------------------------------------------
// get appropriate missile model
// ------------------------------------------------------------------------------
t_combat_actor_model_ptr get_combat_actor_model( t_missile_type missile, double scale_ratio )
{
	static std::vector<t_missile_caches> caches;

	int i;

	for (i = 0; i < caches.size(); i++)
		if (caches[i].get_scale() == scale_ratio)
			break;
	if (i == caches.size())
		caches.push_back( t_missile_caches( scale_ratio ));

	t_combat_actor_model_ptr ptr;

	ptr = caches[i].get( missile );
	if (ptr == 0 && missile != k_missile_magic )
		return get_combat_actor_model( k_missile_magic, scale_ratio );
	return ptr;
}



// ------------------------------------------------------------------------------
// object which handles a set of caches for different scales of the same model
// ------------------------------------------------------------------------------
t_combat_actor_model_cache_set::t_combat_actor_model_cache_set( std::string const& name )
                              : m_model_name( name )
{
}

t_combat_actor_model_ptr t_combat_actor_model_cache_set::get( double scale )
{
	int i;

	for (i = 0; i < m_scales.size(); i++)
		if (m_scales[i] == scale)
			break;
	if (i == m_scales.size())
	{
		m_caches.push_back( t_combat_actor_model_cache( m_model_name, scale ));
		m_scales.push_back( scale );
	}
	return m_caches[i].get();
}

t_combat_actor_model_ptr get_adam_model( double scale_ratio )
{
	static t_combat_actor_model_cache_set caches( "hero.tattooed_white_trash" );

	return caches.get( scale_ratio );
}

t_combat_actor_model_ptr get_jack_model( double scale_ratio )
{
	static t_combat_actor_model_cache_set caches( "hero.jack" );

	return caches.get( scale_ratio );
}

